package com.briup.server.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.briup.common.constant.SystemConstant;
import com.briup.common.response.ResultCode;
import com.briup.common.utils.AssertUtil;
import com.briup.common.utils.BeanUtil;
import com.briup.server.domain.bean.Labs;
import com.briup.server.domain.bean.LabsRentalTime;
import com.briup.server.domain.bean.RentalInfos;
import com.briup.server.domain.dto.LabsRentalTimePageQueryDTO;
import com.briup.server.domain.vo.RentalTimeAvailableVO;
import com.briup.server.domain.vo.RentalTimeVO;
import com.briup.server.mapper.LabsMapper;
import com.briup.server.mapper.LabsRentalTimeMapper;
import com.briup.server.mapper.RentalInfosMapper;
import com.briup.server.service.LabsRentalTimeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;


@Service
@RequiredArgsConstructor
public class LabsRentalTimeServiceImpl extends ServiceImpl<LabsRentalTimeMapper, LabsRentalTime>
        implements LabsRentalTimeService {

    private final LabsMapper labsMapper;
    private final RentalInfosMapper rentalInfosMapper;

    @Override
    public Page<RentalTimeVO> pageQuery(LabsRentalTimePageQueryDTO queryDTO) {
        // 创建分页对象
        Page<LabsRentalTime> page = new Page<>(queryDTO.getPage(), queryDTO.getSize());
        // 构建查询条件
        LambdaQueryWrapper<LabsRentalTime> wrapper = Wrappers.<LabsRentalTime>lambdaQuery()
                .eq(StringUtils.hasText(queryDTO.getLabsId())
                        , LabsRentalTime::getLabsId, queryDTO.getLabsId());
        // 分页查询
        page(page, wrapper);
        // 转换为VO分页对象
        Page<RentalTimeVO> voPage = new Page<>(page.getCurrent(), page.getSize());
        // 设置records
        voPage.setRecords(convertToVO(page.getRecords()).stream()
                .skip((page.getCurrent() - 1) * page.getSize())
                .limit(page.getSize())
                .toList());
        // 设置total
        voPage.setTotal(count(wrapper));
        return voPage;
    }

    @Override
    public void saveOrUpdateLabsRentalTime(LabsRentalTime labsRentalTime) {
        // 检查是否存在
        checkIsExists(labsRentalTime);
        // 新增或更新
        saveOrUpdate(labsRentalTime);
    }

    @Override
    public List<RentalTimeAvailableVO> getAvailableTimes(Long labId, LocalDate date) {
        // 检查实验室是否存在
        Labs labs = labsMapper.selectById(labId);
        AssertUtil.notNull(labs, ResultCode.LABS_IS_NOT_EXISTS);
        // 根据实验室id作为条件，查询出所有的预约时间段
        LambdaQueryWrapper<LabsRentalTime> wrapper = Wrappers.<LabsRentalTime>lambdaQuery()
                .eq(LabsRentalTime::getLabsId, labId);
        // 查询出所有的预约时间段，转换为VO对象
        List<LabsRentalTime> list = list(wrapper);
        List<RentalTimeAvailableVO> voList
                = BeanUtil.convertList(list, RentalTimeAvailableVO.class);

        // 根据日期查询出某一个实验室在某天的所有预约记录
        // 构建查询条件
        LambdaQueryWrapper<RentalInfos> rentalWrapper = Wrappers.<RentalInfos>lambdaQuery()
                .eq(RentalInfos::getLabsId, labId)
                // 只查询已审核通过的预约记录
                .eq(RentalInfos::getApprovalStatus, SystemConstant.ApprovalStatusConstant.APPROVED.getStatus())
                .eq(RentalInfos::getRentalDate, date);
        // 查询出所有的预约记录
        List<RentalInfos> rentalList = rentalInfosMapper.selectList(rentalWrapper);
        // 遍历所有的预约记录，根据时间段id，设置是否可用
        for (RentalTimeAvailableVO vo : voList) {
            // 同时确保，如果选择的日期是当天，预约时间不能在当前时间之前
            if (date.equals(LocalDate.now())) {
                // 确保预约时间在可用时间段内
                LocalTime now = LocalTime.now();
                String[] times = vo.getEnableTime().split("-");
                LocalTime startTime = LocalTime.parse(times[0]);
                LocalTime endTime = LocalTime.parse(times[1]);
                // 确保预约时间在可用时间段内
                vo.setAvailable(now.isBefore(startTime) && now.isBefore(endTime));
            } else if (date.isAfter(LocalDate.now())) {
                // 如果不是在当天之后的时间，预约时间可以在任何时间
                vo.setAvailable(true);
            }
            // 遍历所有的预约记录，根据时间段id，设置是否可用；
            // 在上述基础上进一步确定，确保预约时间段没有被其他预约记录占用
            vo.setAvailable(vo.isAvailable() && rentalList.stream()
                    .noneMatch(rental -> rental.getRentalTimeId().equals(vo.getId())));
        }

        return voList;
    }

    private void checkIsExists(LabsRentalTime labsRentalTime) {
        // 检查是否存在
        if (labsRentalTime.getId() != null) {
            LabsRentalTime rentalTime = getById(labsRentalTime.getId());
            AssertUtil.notNull(rentalTime, ResultCode.LABS_RENTAL_TIME_NOT_FOUND);
        }
        // 检查实验室是否存在
        Labs labs = labsMapper.selectById(labsRentalTime.getLabsId());
        AssertUtil.notNull(labs, ResultCode.LABS_IS_NOT_EXISTS);

        String[] times = labsRentalTime.getEnableTime().split("-");
        LocalTime startTime = LocalTime.parse(times[0]);
        LocalTime endTime = LocalTime.parse(times[1]);

        // 校验当前需要添加的时间段与现有时间段之间是否存在重叠
        LambdaQueryWrapper<LabsRentalTime> overlapWrapper = Wrappers.<LabsRentalTime>lambdaQuery()
                .eq(LabsRentalTime::getLabsId, labsRentalTime.getLabsId())
                .ne(labsRentalTime.getId() != null, LabsRentalTime::getId, labsRentalTime.getId())
                .apply("TIME_TO_SEC(SUBSTRING_INDEX(enable_time, '-', 1)) <= TIME_TO_SEC({0}) AND " +
                       "TIME_TO_SEC(SUBSTRING_INDEX(enable_time, '-', -1)) >= TIME_TO_SEC({1})", endTime, startTime);
        // 检查是否存在重叠
        long count = count(overlapWrapper);
        AssertUtil.isTrue(count == 0, ResultCode.LABS_RENTAL_TIME_OVERLAP);

        // 实验室和预约时间段必须是联合唯一的
        LambdaQueryWrapper<LabsRentalTime> wrapper = Wrappers.<LabsRentalTime>lambdaQuery()
                .eq(LabsRentalTime::getLabsId, labsRentalTime.getLabsId())
                .eq(LabsRentalTime::getEnableTime, labsRentalTime.getEnableTime());
        // 检查是否存在
        long count2 = count(wrapper);
        AssertUtil.isTrue(count2 == 0, ResultCode.LABS_RENTAL_TIME_ALREADY_EXISTS);
    }

    private List<RentalTimeVO> convertToVO(List<LabsRentalTime> records) {
        // 获取所有的实验室id
        List<String> labsIds = records.stream()
                .map(LabsRentalTime::getLabsId).toList();
        // 查询所有实验室
        List<Labs> labsList = labsMapper.selectByIds(labsIds);
        // 转换为VO对象
        return records.stream()
                .map(rentalTime -> {
                    Labs labs = labsList.stream()
                            .filter(lab -> lab.getId().equals(rentalTime.getLabsId()))
                            .findFirst()
                            .orElse(null);
                    RentalTimeVO timeVO = BeanUtil.convert(rentalTime, RentalTimeVO.class);
                    timeVO.setLabsName(labs != null ? labs.getLabsName() : null);
                    return timeVO;
                }).toList();
    }
}
